package com.doubao.auth.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.doubao.auth.client.TokenServiceClient;
import com.doubao.auth.config.JwtConfig;
import com.doubao.auth.dto.JwtAuthResponse;
import com.doubao.auth.dto.PhoneLoginRequest;
import com.doubao.auth.entity.AuthUser;
import com.doubao.auth.mapper.AuthUserMapper;
import com.doubao.auth.util.JwtUtils;
import com.doubao.common.result.Result;
import com.doubao.common.utils.RedisTemplateFactory;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

@Service
@RequiredArgsConstructor
@Slf4j
public class PhoneAuthService {

    private final JwtUtils jwtUtils;
    private final AuthUserMapper authUserMapper;
    private RedisTemplate<String, String> redisTemplate;
    private final JwtConfig jwtConfig;
    private final VerificationCodeService verificationCodeService;
    private final PasswordEncoder passwordEncoder;
    private final TokenServiceClient tokenService;

    private static final String REFRESH_TOKEN_KEY_PREFIX = "refresh:token:";

    // 使用ConcurrentHashMap作为线程安全的内存缓存
    private final ConcurrentHashMap<String, RefreshTokenInfo> localRefreshTokenCache = new ConcurrentHashMap<>();

    /**
     * 刷新令牌信息类
     */
    private static class RefreshTokenInfo {
        private final String token;
        private final long expireTime;

        public RefreshTokenInfo(String token, long expireDays) {
            this.token = token;
            this.expireTime = System.currentTimeMillis() + (expireDays * 24 * 60 * 60 * 1000L);
        }

        public boolean isExpired() {
            return System.currentTimeMillis() > expireTime;
        }

        public String getToken() {
            return token;
        }
    }

    @PostConstruct
    public void init() {
        this.redisTemplate = RedisTemplateFactory.getStringRedisTemplate();
        log.info("PhoneAuthService使用RedisTemplateFactory获取StringRedisTemplate");

        // 启动定期清理过期缓存的任务
        scheduleLocalCacheCleanup();
    }

    /**
     * 定期清理过期的本地缓存
     */
    private void scheduleLocalCacheCleanup() {
        Thread cleanupThread = new Thread(() -> {
            while (true) {
                try {
                    // 每小时清理一次
                    Thread.sleep(TimeUnit.HOURS.toMillis(1));

                    int beforeSize = localRefreshTokenCache.size();
                    localRefreshTokenCache.entrySet().removeIf(entry -> entry.getValue().isExpired());
                    log.info("清理本地刷新令牌缓存，清理前: {}，清理后: {}", beforeSize, localRefreshTokenCache.size());
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                } catch (Exception e) {
                    log.error("清理本地刷新令牌缓存时发生错误", e);
                }
            }
        });
        cleanupThread.setDaemon(true);
        cleanupThread.setName("refresh-token-cache-cleanup");
        cleanupThread.start();
    }

    /**
     * 根据手机号查询用户信息（带缓存）
     */
    @Cacheable(value = "userPhone", key = "#phoneNumber", unless = "#result == null")
    public AuthUser getUserByPhone(String phoneNumber) {
        if (StringUtils.isBlank(phoneNumber)) {
            return null;
        }
        LambdaQueryWrapper<AuthUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AuthUser::getPhone, phoneNumber);
        return authUserMapper.selectOne(queryWrapper);
    }

    /**
     * 手机号验证码登录
     */
    @Transactional
    public Result<JwtAuthResponse> loginByPhone(PhoneLoginRequest request) {
        try {
            // 验证码校验
            boolean isValid = verificationCodeService.verifyCode(request.getPhoneNumber(), request.getCode());
            if (!isValid) {
                return Result.failed("验证码错误或已过期");
            }

            // 使用缓存方法查询用户
            AuthUser user = getUserByPhone(request.getPhoneNumber());

            // 如果用户不存在，则自动注册
            boolean isNewUser = false;
            if (user == null) {
                user = registerNewUser(request.getPhoneNumber());
                isNewUser = true;
            }

            // 生成令牌
            String token = jwtUtils.generateToken(user.getUsername(), user.getId());
            String refreshToken = jwtUtils.generateRefreshToken(user.getUsername());

            // 存储刷新令牌，使用优化的方式
            storeRefreshToken(user.getUsername(), refreshToken);

            // 异步更新用户登录信息
            final Long userId = user.getId();
            final boolean finalIsNewUser = isNewUser;
            CompletableFuture.runAsync(() -> {
                try {
                    AuthUser userToUpdate = authUserMapper.selectById(userId);
                    if (userToUpdate != null) {
                        userToUpdate.setLastLoginTime(LocalDateTime.now());
                        userToUpdate.setLastLoginIp(request.getIp());
                        userToUpdate.setLoginCount(userToUpdate.getLoginCount() + 1);
                        authUserMapper.updateById(userToUpdate);

                        // 如果是新用户，初始化token余额
                        if (finalIsNewUser) {
                            tokenService.initializeUserTokens(userId);
                        }
                    }
                } catch (Exception e) {
                    log.error("异步更新用户登录信息失败: {}", e.getMessage(), e);
                }
            });

            // 构建响应对象
            JwtAuthResponse authResponse = new JwtAuthResponse();
            authResponse.setToken(token);
            authResponse.setRefreshToken(refreshToken);
            authResponse.setUserId(user.getId());
            authResponse.setUsername(user.getUsername());
            authResponse.setNickname(user.getNickname());
            authResponse.setAvatar(user.getAvatar());
            authResponse.setVipStatus(user.getVipStatus());

            return Result.success(authResponse);
        } catch (Exception e) {
            log.error("手机号登录失败: {}", e.getMessage(), e);
            return Result.failed("登录失败: " + e.getMessage());
        }
    }

    /**
     * 存储刷新令牌到Redis或本地缓存
     */
    private void storeRefreshToken(String username, String refreshToken) {
        // 总是先保存到本地缓存
        localRefreshTokenCache.put(username, new RefreshTokenInfo(refreshToken, jwtConfig.getRefreshExpiration()));

        // 尝试存储到Redis，使用优化的方式
        if (RedisTemplateFactory.isConnectionHealthy()) {
            try {
                // 使用工厂的安全执行方法
                RedisTemplateFactory.executeWithFallback(
                        () -> {
                            String key = REFRESH_TOKEN_KEY_PREFIX + username;
                            redisTemplate.opsForValue().set(
                                    key,
                                    refreshToken,
                                    jwtConfig.getRefreshExpiration(),
                                    TimeUnit.DAYS
                            );
                            log.debug("刷新令牌已存储到Redis: {}", username);
                            return null;
                        },
                        () -> {
                            log.debug("Redis不可用，刷新令牌仅存储在本地缓存: {}", username);
                            return null;
                        }
                );
            } catch (Exception e) {
                log.warn("存储刷新令牌到Redis失败: {}", e.getMessage());
            }
        } else {
            log.debug("Redis连接不健康，刷新令牌仅存储在本地缓存: {}", username);
        }
    }

    /**
     * 获取刷新令牌
     */
    public String getRefreshToken(String username) {
        if (StringUtils.isBlank(username)) {
            return null;
        }

        // 首先检查本地缓存
        RefreshTokenInfo tokenInfo = localRefreshTokenCache.get(username);
        if (tokenInfo != null && !tokenInfo.isExpired()) {
            log.debug("从本地缓存获取刷新令牌: {}", username);
            return tokenInfo.getToken();
        }

        // 如果本地缓存没有，尝试从Redis获取
        if (RedisTemplateFactory.isConnectionHealthy()) {
            try {
                // 使用安全的Redis访问方式
                return RedisTemplateFactory.executeWithFallback(
                        () -> redisTemplate.opsForValue().get(REFRESH_TOKEN_KEY_PREFIX + username),
                        () -> null  // Redis不可用时返回null
                );
            } catch (Exception e) {
                log.warn("从Redis获取刷新令牌失败: {}", e.getMessage());
            }
        }

        return null;
    }

    /**
     * 注册新用户
     * @param phoneNumber 手机号
     * @return 新用户信息
     */
    private AuthUser registerNewUser(String phoneNumber) {
        AuthUser user = new AuthUser();
        user.setUuid(UUID.randomUUID().toString().replace("-", ""));

        // 使用手机号后4位作为用户名前缀
        String userNamePrefix = "user_" + phoneNumber.substring(phoneNumber.length() - 4);

        // 确保用户名唯一
        String username = userNamePrefix;
        int suffix = 1;
        while (checkUsernameExists(username)) {
            username = userNamePrefix + "_" + suffix++;
        }

        user.setUsername(username);

        // 随机密码
        String randomPassword = UUID.randomUUID().toString().substring(0, 16);
        user.setPassword(passwordEncoder.encode(randomPassword));

        user.setNickname(username);
        user.setPhone(phoneNumber);
        user.setStatus(1); // 正常状态
        user.setDeleted(0); // 未删除
        user.setVipStatus(0); // 非VIP
        user.setLoginCount(1); // 首次登录
        user.setTotalTokensUsed(0L); // 初始token使用量
        user.setTotalTokensBalance(99999999); // 初始余额设为0，由token服务统一初始化
        user.setRegisterSource("phone"); // 注册来源
        user.setCreatedAt(LocalDateTime.now());
        user.setUpdatedAt(LocalDateTime.now());

        authUserMapper.insert(user);
        return user;
    }

    /**
     * 检查用户名是否存在
     * @param username 用户名
     * @return 是否存在
     */
    private boolean checkUsernameExists(String username) {
        if (StringUtils.isBlank(username)) {
            return false;
        }

        LambdaQueryWrapper<AuthUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AuthUser::getUsername, username);
        return authUserMapper.selectCount(queryWrapper) > 0;
    }
}